package com.infolab.aims.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.infolab.aims.GlobalConstant;
import com.infolab.aims.common.PaginationHelper;
import com.infolab.aims.common.QueryHelper;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;
import com.infolab.aims.domain.LocationBean;
import com.infolab.aims.domain.SystemCodeType;
import com.infolab.aims.domain.SystemCodeValue;
import com.infolab.aims.service.intf.SystemCodeService;


@Service
@Transactional
public class SystemCodeServiceImpl implements SystemCodeService {
	
	Log logger = LogFactory.getLog(getClass());
	@PersistenceContext
	private EntityManager em;
	
	public void createSystemCodeType(SystemCodeType systemCodeType)
	{
		if(systemCodeType.getAccessDepartment()==null || systemCodeType.getAccessDepartment().getDepartmentId()==null)
			systemCodeType.setAccessDepartment(null);
		
		SystemCodeType newSystemCodeType = new SystemCodeType();
		
		systemCodeType.setModifiedBy(systemCodeType.getCreatedBy());
		systemCodeType.setDateCreated(new Date());
		systemCodeType.setDateModified(new Date());
		
		
		BeanUtils.copyProperties(systemCodeType,newSystemCodeType);
		
		em.persist(newSystemCodeType);
		
		systemCodeType.setSystemCodeTypeId(newSystemCodeType.getSystemCodeTypeId());
		
	}

	public void updateSystemCodeType(SystemCodeType systemCodeType) {
		
		if(systemCodeType.getAccessDepartment()==null || systemCodeType.getAccessDepartment().getDepartmentId()==null)
			systemCodeType.setAccessDepartment(null);
		
		SystemCodeType newSystemCodeType = em.find(SystemCodeType.class, systemCodeType.getSystemCodeTypeId());

		systemCodeType.setDateModified(new Date());
		
		BeanUtils.copyProperties(systemCodeType,newSystemCodeType);
		
		em.merge(newSystemCodeType);
	}
	
	public void updateSystemCodeLocation(LocationBean locationBean) {
		//first RepairTransferLocation
		SearchCriteria searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("active", true);  
		searchCriteria.addSearchCriteria("systemCodeTypeCode", GlobalConstant.SYSTEM_CODE_TYPE_REPAIR_TRANSFER_LOCATION);
		SystemCodeType type1 = findSystemCodeType(searchCriteria);
		type1.setSystemCodeTypeValue(StringUtils.join(locationBean.getRepairTransferLocation(), ","));
		em.merge(type1);
		//second WorkOrderAdHoc
		searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("active", true);  
		searchCriteria.addSearchCriteria("systemCodeTypeCode", GlobalConstant.SYSTEM_CODE_TYPE_COMPONENT_ISSUE_MAIN_LOCATION);
		SystemCodeType type2 = findSystemCodeType(searchCriteria);
		type2.setSystemCodeTypeValue(locationBean.getComponentIssueMainLocation());
		em.merge(type2);
		//third WorkOrderFaultLocation
		searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("active", true);  
		searchCriteria.addSearchCriteria("systemCodeTypeCode", GlobalConstant.SYSTEM_CODE_TYPE_WORKORDER_FAULT_LOCATION);
		SystemCodeType type3 = findSystemCodeType(searchCriteria);
		type3.setSystemCodeTypeValue(locationBean.getWorkOrderFaultLocation());
		em.merge(type3);
		//fouth WorkOrderInVehicleLocation
		searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("active", true);  
		searchCriteria.addSearchCriteria("systemCodeTypeCode", GlobalConstant.SYSTEM_CODE_TYPE_WORKORDER_INVEHICLE_LOCATION);
		SystemCodeType type4 = findSystemCodeType(searchCriteria);
		type4.setSystemCodeTypeValue(locationBean.getWorkOrderInVehicleLocation());
		em.merge(type4);
		//fifth WorkOrderActiveLocation
		searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("active", true);  
		searchCriteria.addSearchCriteria("systemCodeTypeCode", GlobalConstant.SYSTEM_CODE_TYPE_WORKORDER_ACTIVE_LOCATION);
		SystemCodeType type5 = findSystemCodeType(searchCriteria);
		type5.setSystemCodeTypeValue(locationBean.getWorkOrderActiveLocation());
		em.merge(type5);
	}


	public SystemCodeType getSystemCodeType(Integer systemCodeTypeId) {
		
		return em.find(SystemCodeType.class, systemCodeTypeId);
	}
	
	public SystemCodeType findSystemCodeType(SearchCriteria searchCriteria) {
		List<SystemCodeType> systemCodeTypes = findSystemCodeTypes(searchCriteria);
		if(CollectionUtils.isEmpty(systemCodeTypes)){
			return null;
		}
		return systemCodeTypes.get(0);
	
	}

	public List<SystemCodeType> findSystemCodeTypes(SearchCriteria searchCriteria) {
		if(searchCriteria==null) searchCriteria = new SearchCriteria();
		CriteriaBuilder cb = em.getCriteriaBuilder();
		
		CriteriaQuery<SystemCodeType> cq = cb.createQuery(SystemCodeType.class);
		Root<SystemCodeType> root = cq.from(SystemCodeType.class);
		
		List<Predicate> criteria = new ArrayList<Predicate>();
		if (searchCriteria.getSearchCriteria("systemCodeTypeCode")!= null) {
			ParameterExpression<String> p = cb.parameter(String.class, "systemCodeTypeCode");
			criteria.add(cb.equal(root.get("systemCodeTypeCode"),  searchCriteria.getSearchCriteria("systemCodeTypeCode")));
		}
		
		if (searchCriteria.getSearchCriteria("active")!= null) {
			ParameterExpression<Boolean> p = cb.parameter(Boolean.class, "active");
			criteria.add(cb.equal(root.get("active"),  searchCriteria.getSearchCriteria("active")));
		}
		
		cq.select(root);
		if (criteria.size() == 0) {
			} else if (criteria.size() == 1) {
			cq.where(criteria.get(0));
			} else {
			cq.where(cb.and(criteria.toArray(new Predicate[0])));
		}
		Query query = em.createQuery(cq);
		
		if(searchCriteria.getMaxRow()!=null)
			query.setMaxResults(searchCriteria.getMaxRow());
		
		return query.getResultList();
	}

	public SearchResult searchSystemCodeType(
			SearchCriteria searchCriteria) {
		
		if(searchCriteria.getCurPage()==null)
			searchCriteria.setCurPage(1);
		
		SystemCodeTypePaginationHelper helper = new SystemCodeTypePaginationHelper();
		
		return helper.getSearchResultFromQueryHelper(em, searchCriteria);
	}
	
	public static class SystemCodeTypePaginationHelper extends PaginationHelper<SystemCodeType> {

		@Override
		public String[] getValidColumnName() {
			return new String[]{"systemCodeTypeCode","systemCodeTypeDescription","accessDepartment.departmentName","accessDepartmentId"};
		}
		
		@Override
		public String[] getValidSortName() {
			return new String[]{"systemCodeTypeCode","systemCodeTypeDescription","systemCodeType.modifiedBy","systemCodeType.dateModified","accessDepartment.departmentName","systemCodeType.active"};
		}

		@Override
		public String getDefaultSortName() {
			return "systemCodeTypeCode";
		}
		
		@Override
		public QueryHelper getQueryHelper() {
			QueryHelper queryHelper = new QueryHelper();
			queryHelper.select("select systemCodeType from SystemCodeType systemCodeType left join systemCodeType.accessDepartment accessDepartment");
			return queryHelper;
		}
		
	}

	public void updateSystemCodeValues(SystemCodeType systemCodeType) {
		
		List<SystemCodeValue> systemCodeValuesForm = systemCodeType.getSystemCodeValuesForm();
		if(systemCodeValuesForm!=null) {
			for (int i = 0; i < systemCodeValuesForm.size(); i++) {
				SystemCodeValue systemCodeValue = systemCodeValuesForm.get(i);
				systemCodeValue.setSystemCodeType(systemCodeType);
				
				if(systemCodeValue.getSystemCodeValueId()==null)
				{
					if(!StringUtils.equalsIgnoreCase(systemCodeValue.getSystemCode1(),"$DELETED$"))
					{
						systemCodeValue.setCreatedBy(systemCodeType.getModifiedBy());
						systemCodeValue.setDateCreated(systemCodeType.getDateModified());
						em.persist(systemCodeValue);
					}
				}
				else
				{
					if(StringUtils.equalsIgnoreCase(systemCodeValue.getSystemCode1(),"$DELETED$"))
					{
						systemCodeValue = em.getReference(SystemCodeValue.class,systemCodeValue.getSystemCodeValueId());
						em.remove(systemCodeValue);
					}
					else
					{
						systemCodeValue.setModifiedBy(systemCodeType.getModifiedBy());
						systemCodeValue.setDateModified(systemCodeType.getDateModified());
						em.merge(systemCodeValue);
					}
				}
					
			}
		}
	}
	
	public void updateFaultCode(String faultCode,SystemCodeType systemCodeType) {

		Query query = em.createQuery("delete from SystemCodeValue systemCodeValue where systemCode1=:systemCode1 and systemCodeValue.systemCodeType.systemCodeTypeId=:systemCodeTypeId");
		query.setParameter("systemCode1", faultCode);
		query.setParameter("systemCodeTypeId", systemCodeType.getSystemCodeTypeId());
		query.executeUpdate();
		
		List<SystemCodeValue> systemCodeValuesForm = systemCodeType.getSystemCodeValuesForm();
		
		if(systemCodeValuesForm!=null) {
			for (int i = 0; i < systemCodeValuesForm.size(); i++) {
				SystemCodeValue systemCodeValue = systemCodeValuesForm.get(i);
				em.persist(systemCodeValue);
					
			}
		}
	}


	public List<SystemCodeValue> findSystemCodeCodeValues(SearchCriteria searchCriteria) {
		
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<SystemCodeValue> cq = cb.createQuery(SystemCodeValue.class);
		Root<SystemCodeValue> root = cq.from(SystemCodeValue.class);
		List<Predicate> criteria = new ArrayList<Predicate>();
		
		Join<SystemCodeValue,SystemCodeType> systemCodeType =
			root.join("systemCodeType", JoinType.INNER);
		
		
		if (searchCriteria.getSearchCriteria("systemCodeValueDescription")!= null) {
			ParameterExpression<String> p =
				cb.parameter(String.class, "systemCodeValueDescription");
			criteria.add(cb.like(cb.upper(root.<String>get("systemCodeValueDescription")), cb.upper(p)));
		}
		if (searchCriteria.getSearchCriteria("systemCodeTypeCode")!= null) {
			ParameterExpression<String> p =
				cb.parameter(String.class, "systemCodeTypeCode");
				criteria.add(cb.equal(systemCodeType.get("systemCodeTypeCode"), p));
		}
		
		cq.select(root);
		if (criteria.size() == 0) {
			} else if (criteria.size() == 1) {
			cq.where(criteria.get(0));
			} else {
			cq.where(cb.and(criteria.toArray(new Predicate[0])));
		}
		
		if(searchCriteria.getSortType()!=null)
		{
			if(searchCriteria.isAscendingSort())
				cq.orderBy(cb.asc(root.get(searchCriteria.getSortName())));
			else
				cq.orderBy(cb.desc(root.get(searchCriteria.getSortName())));	
		}
		
		Query query = em.createQuery(cq);
		
		if (searchCriteria.getSearchCriteria("systemCodeValueDescription")!= null) {
			query.setParameter("systemCodeValueDescription", "%"+searchCriteria.getSearchCriteria("systemCodeValueDescription")+"%");
		}
		if (searchCriteria.getSearchCriteria("systemCodeTypeCode")!= null) {
			query.setParameter("systemCodeTypeCode", searchCriteria.getSearchCriteria("systemCodeTypeCode"));
		}
		
		if(searchCriteria.getMaxRow()!=null)
			query.setMaxResults(searchCriteria.getMaxRow());
		
		return query.getResultList();
		
	}

	
}
